package objetos;

import interfaces.Atividade;
import interfaces.IGenericObject;

import java.sql.Date;
import java.util.ArrayList;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import objetos.Emprestimo.StatusEmprestimo;
import objetos.Item.StatusItem;
import sistema.GerenciadorDeDados;
import sistema.GerenciadorDeMensagens;
import sistema.GerenciadorPedidos;
import sistema.RepositorioDeDados;
import util.Data;
import util.OrdenaPesquisa;
import util.Validador;
import factory.DAOFactory;
import factory.EmprestimoFactory;
import factory.ItemFactory;
import factory.UsuarioFactory;

/**
 * 
 * @author  Filipe Alencar, Felipe Cavalcanti, Rodrigo L. de Brito, Thiago Gonçalves, Victor Hugo Arcelino.
 *
 */

//TODO substituir todos os locais onde o gregorian calendar foi usado por algo equivalente, como Date, Gregorian nao eh emulado pelo gwt

public class Sistema {


	private static RepositorioDeDados _repositorio = RepositorioDeDados.getInstance();
	private HashMap<Integer, Usuario> listaDeSessoesAbertas = new HashMap<Integer, Usuario>();
	private GerenciadorDeDados gerenciadorUsuarios = new GerenciadorDeDados(DAOFactory.createUsuarioDAO());
	private GerenciadorDeDados gerenciadorItems = new GerenciadorDeDados(DAOFactory.createItemDAO());
	private GerenciadorDeDados gerenciadorEmprestimos = new GerenciadorDeDados(DAOFactory.createEmprestimoDAO());
//	private GregorianCalendar dataSistema = new GregorianCalendar(2011,8,23);
	@SuppressWarnings("deprecation")
	private Data dataSistema = new Data(new Date(2011,8,23));
	private static HistoricoDeAtividades _historico = HistoricoDeAtividades.getInstance();
	private static Sistema _sistema;

	public Sistema(){

	}
	
	public String teste(){
		return "Ligação projeto -> interface web feita!";
	}
	/**
	 * Implementa o padrao Singleton
	 * @return Uma unica instancia de Sistema.
	 */
	public static Sistema getInstance(){
		return(_sistema == null)? _sistema= new Sistema():_sistema;
	}


	public void criarUsuario(String login, String nome, String endereco) throws Exception{
		Usuario usuario = UsuarioFactory.createUsuario(login, nome, endereco);
		gerenciadorUsuarios.cadastrar(usuario);

	}

	public void verificarSessao(String idSessao) throws Exception{
		if(!(Validador.validarString(idSessao)))
			throw new Exception("Sessão inválida");
		Integer idSessaoInt;
		try{
			idSessaoInt = Integer.parseInt(idSessao);
		}catch(Exception e){
			throw new Exception("Sessão inexistente");
		}
		if(!(listaDeSessoesAbertas.keySet().contains(idSessaoInt)))
			throw new Exception("Sessão inexistente");		
	}


	public void verificarIdItem(String idItem) throws Exception{
		if(!(Validador.validarString(idItem)))
			throw new Exception("Identificador do item é inválido");
		Integer idItemInt;
		try{
			idItemInt = Integer.parseInt(idItem);
		}catch(Exception e){
			throw new Exception("Item inexistente");
		}
		List<IGenericObject> items = gerenciadorItems.pesquisar("id", "" + idItemInt);
		if(items.isEmpty())
			throw new Exception("Item inexistente");

	}

	private void verificarIdRequisicaoEmprestimo(String idRequisicaoEmprestimo) throws Exception{
		if(!(Validador.validarString(idRequisicaoEmprestimo))){
			throw new Exception("Identificador da requisição de empréstimo é inválido");
		}
		try{
			Integer.parseInt(idRequisicaoEmprestimo);
		}catch(Exception e){
			throw new Exception("Requisição de empréstimo inexistente");
		}
		List<IGenericObject> emprestimos = gerenciadorEmprestimos.pesquisar("idRequisicaoEmprestimo", idRequisicaoEmprestimo);
		if(emprestimos.isEmpty()){
			throw new Exception("Requisição de empréstimo inexistente");
		}

	}


	private void verificarIdEmprestimo(String idEmprestimo) throws Exception{
		if(!(Validador.validarString(idEmprestimo))){
			throw new Exception("Identificador do empréstimo é inválido");
		}
		try{
			Integer.parseInt(idEmprestimo);
		}catch(Exception e){
			throw new Exception("Empréstimo inexistente");
		}
		List<IGenericObject> emprestimos = gerenciadorEmprestimos.pesquisar("idEmprestimo", idEmprestimo);
		if(emprestimos.isEmpty()){
			throw new Exception("Empréstimo inexistente");
		}
	}
	
	private void verificarPublicacaoPedido(String idPublicacaoPedido) throws Exception{
		if(!(Validador.validarString(idPublicacaoPedido))){
			throw new Exception("Identificador da publicação de pedido é inválido");
		}
		try{
			Integer.parseInt(idPublicacaoPedido);
		}catch(Exception e){
			throw new Exception("Publicação de pedido inexistente");
		}
		
	}

	public Integer cadastrarItem(String idSessao, String nome, String descricao, String categoria)throws Exception{
		verificarSessao(idSessao);
		int idItem = gerarId();
		Usuario donoDoItem = retornaUsuarioPeloIdSessao(idSessao);
		/* Verifica se o Id ja nao está sendo usado */
		List<IGenericObject> items = gerenciadorItems.pesquisar("id", ""+idItem);
		while(items.size()>0){
			idItem = gerarId();
			items = gerenciadorItems.pesquisar("id", ""+idItem);
		}
		Item item = ItemFactory.createItem(listaDeSessoesAbertas.get(Integer.parseInt(idSessao)), nome, descricao, categoria, idItem);
		gerenciadorItems.cadastrar(item);
		donoDoItem.adicionarItem(item); 
		Atividade atividade = new AtividadeDeCadastrarItem(donoDoItem.getNome(), item.getNome());
		_historico.addAtividades(donoDoItem, atividade);
		return idItem;
	}

	public int abrirSessao(String login) throws Exception{
		if(!(Validador.validarString(login)))
			throw new Exception("Login inválido");
		List<IGenericObject> usuarios = gerenciadorUsuarios.pesquisar("login", login);
		if(usuarios.isEmpty()){
			throw new Exception("Usuário inexistente");
		}else{
			int idSessao = gerarId();
			while(listaDeSessoesAbertas.containsKey(idSessao))
				idSessao = gerarId();
			listaDeSessoesAbertas.put(idSessao, (Usuario) usuarios.get(0));
			return idSessao;
		}

	}

	public String getAtributoUsuario(String login, String atributo)throws Exception{
		if(!(Validador.validarString(login)))
			throw new Exception("Login inválido");
		if(!(Validador.validarString(atributo)))
			throw new Exception("Atributo inválido");

		Usuario usuario = retornaUsuarioPeloLogin(login);
		if (atributo.equals("nome")){
			return usuario.getNome();
		}
		if (atributo.equals("endereco")){
			return usuario.getEndereco();
		}else{
			throw new Exception("Atributo inexistente");
		}

	}

	public String getAtributoItem(String idItem, String atributo)throws Exception{
		if(!(Validador.validarString(idItem)))
			throw new Exception("Identificador do item é inválido");
		if(!(Validador.validarString(atributo)))
			throw new Exception("Atributo inválido");
		Item item;
		try{
			item = retornaItemPeloId(Integer.parseInt(idItem));
		}catch(Exception e){
			throw new Exception("Item inexistente");
		}
		if (atributo.equals("nome")){
			return item.getNome();
		}
		if (atributo.equals("descricao")){
			return item.getDescricao();
		}
		if (atributo.equals("categoria")){
			return item.getNameCategoriaFromCategoria(item.getTipoDeItem());
		}else{
			throw new Exception("Atributo inexistente");
		}

	}

	public int gerarId(){
		Random rand = new Random();
		int id = 100000+rand.nextInt(899999);
		return id;
	}

	public Usuario retornaUsuarioPeloLogin(String login)throws Exception{
		List<IGenericObject> usuarios = gerenciadorUsuarios.pesquisar("login", login);
		if(usuarios.isEmpty()){
			throw new Exception("Usuário inexistente");
		}else{
			Usuario user = (Usuario) usuarios.get(0);
			return user;
		}

	}

	private Usuario retornaUsuarioPeloIdSessao(String idSessao){
		return listaDeSessoesAbertas.get(Integer.parseInt(idSessao));
	}
	
	public Usuario retornaUsuarioPeloNome(String nome)throws Exception{
		List<IGenericObject> usuarios = gerenciadorUsuarios.pesquisar("nome", nome);
		if(usuarios.isEmpty()){
			throw new Exception("Usuário inexistente");
		}else{
			Usuario user = (Usuario) usuarios.get(0);
			return user;
		}

	}

	public Item retornaItemPeloId(Integer id)throws Exception{
		List<IGenericObject> items = gerenciadorItems.pesquisar("id", ""+id);
		if(items.isEmpty()){
			throw new Exception("Item inexistente");
		}else{
			Item item = (Item) items.get(0);
			return item;
		}

	}

	public String localizarUsuario(String idSessao, String chave, String atributo)throws Exception{
		verificarSessao(idSessao);
		if (!(Validador.validarString(chave)))
			throw new Exception("Palavra-chave inválida");
		if(!(Validador.validarString(atributo)))
			throw new Exception("Atributo inválido");
		// O usuario nao deve se encontrar!
		List<IGenericObject> usuarios = gerenciadorUsuarios.pesquisar(atributo, chave);
		String resultadoPesquisa = "";
		Usuario usuario;
		// For inverso para passar o US03
		//for (int i = usuarios.size()-1 ; i >= 0 ; i--){
		for (int i = 0 ; i < usuarios.size() ; i++){
			usuario = (Usuario)usuarios.get(i);
			/* Se o usuario for o mesmo que acessou o metodo, ignora o mesmo. */
			if (usuario.equals(listaDeSessoesAbertas.get(Integer.parseInt(idSessao))))
				continue;
			if(!(resultadoPesquisa.equals("")))
				resultadoPesquisa+="; ";
			resultadoPesquisa+=usuario.getNome()+" - "+usuario.getEndereco();
		}
		if (resultadoPesquisa.equals(""))
			return "Nenhum usuário encontrado";
		return resultadoPesquisa;
	}

	public void requisitarAmizade(String idSessao, String login) throws Exception{
		verificarSessao(idSessao);
		if(!(Validador.validarString(login)))
			throw new Exception("Login inválido");
		try{
			retornaUsuarioPeloLogin(login);
		}catch(Exception e){
			throw new Exception("Usuário inexistente");
		}
		Usuario usuarioRequisitado = retornaUsuarioPeloLogin(login);
		usuarioRequisitado.adicionarAmizadePendente(listaDeSessoesAbertas.get(Integer.parseInt(idSessao)));
	}

	public void aprovarAmizade(String idSessao, String login) throws Exception{
		verificarSessao(idSessao);
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		List<Usuario> listaAmigosPendentes = listaDeSessoesAbertas.get(Integer.parseInt(idSessao)).getListaDeAmigosPendentes();
		if(!(Validador.validarString(login)))
			throw new Exception("Login inválido");
		Usuario usuarioParaAceitar = null;
		try{
			usuarioParaAceitar = retornaUsuarioPeloLogin(login);
		}catch(Exception e){
			throw new Exception("Usuário inexistente");
		}
		if((!listaAmigosPendentes.contains(usuarioParaAceitar)) && verificaAmizade(idSessao, login).equals("false")){
			throw new Exception("Requisição de amizade inexistente");
		}
		else if(verificaAmizade(idSessao, login).equals("true")){
			throw new Exception("Os usuários já são amigos");
		}
		this.listaDeSessoesAbertas.get(Integer.parseInt(idSessao)).aprovarAmizade(usuarioParaAceitar);
		usuarioParaAceitar.adicionarAmigo(this.listaDeSessoesAbertas.get(Integer.parseInt(idSessao)));
		Atividade atividadeDoUsuario = new AtividadeDeAdicaoDeAmigo(usuario.getNome(), usuarioParaAceitar.getNome());
		Atividade atividadeDoAmigo = new AtividadeDeAdicaoDeAmigo(usuarioParaAceitar.getNome(), usuario.getNome());
		_historico.addAtividades(usuarioParaAceitar, atividadeDoAmigo);
		_historico.addAtividades(usuario, atividadeDoUsuario );

	}

	public String verificaAmizade(String idSessao, String login) throws Exception{
		verificarSessao(idSessao);
		if(!(Validador.validarString(login)))
			throw new Exception("Login inválido");
		try{
			retornaUsuarioPeloLogin(login);
		}catch(Exception e){
			throw new Exception("Usuário inexistente");
		}
		if (this.listaDeSessoesAbertas.get(Integer.parseInt(idSessao)).ehAmigo(retornaUsuarioPeloLogin(login)))
			return "true";
		return "false";
	}

	public String getRequisicoesDeAmizade(String idSessao) throws Exception{
		verificarSessao(idSessao);
		String requisicoesDeAmizade = "";
		List<Usuario> listaRequisicoesDeAmizades = listaDeSessoesAbertas.get(Integer.parseInt(idSessao)).getListaDeAmigosPendentes();
		if(listaRequisicoesDeAmizades.isEmpty()){
			return "Não há requisições";
		}else{
			for (Usuario usuario : listaRequisicoesDeAmizades) {
				requisicoesDeAmizade += usuario.getLogin() + "; ";
			}

		}
		requisicoesDeAmizade = requisicoesDeAmizade.substring(0, requisicoesDeAmizade.length() - 2);
		return requisicoesDeAmizade;

	}

	public String getAmigos(String idSessao) throws Exception{
		verificarSessao(idSessao);
		String amigos = "";
		List<Usuario> listaDeAmigos = listaDeSessoesAbertas.get(Integer.parseInt(idSessao)).getListaDeAmigosAdicionados();
		if(listaDeAmigos.isEmpty()){
			return "O usuário não possui amigos";
		}else{
			for (Usuario usuario : listaDeAmigos) {
				amigos += usuario.getLogin() + "; ";
			}

		}
		amigos = amigos.substring(0, amigos.length() - 2);
		return amigos;

	}

	public String getAmigos(String idSessao, String login) throws Exception{
		if(!(Validador.validarString(login)))
			throw new Exception("Login inválido");
		verificarSessao(idSessao);
		String amigos = "";
		Usuario alvo = null;
		try{
			alvo = retornaUsuarioPeloLogin(login);
		}catch(Exception e){
			throw new Exception("Usuário inexistente");
		}
		List<Usuario> listaDeAmigos = alvo.getListaDeAmigosAdicionados();
		if(listaDeAmigos.isEmpty()){
			return "O usuário não possui amigos";
		}else{
			for (Usuario usuario : listaDeAmigos) {
				amigos += usuario.getLogin() + "; ";
			}

		}
		amigos = amigos.substring(0, amigos.length() - 2);
		return amigos;



	}

	public HashMap<Integer, Usuario> getListaDeSessoesAbertas() {
		return listaDeSessoesAbertas;
	}


	public String getItens(String idSessao) throws Exception{
		verificarSessao(idSessao);
		String itens = "";
		List<Item> listaDeItens = listaDeSessoesAbertas.get(Integer.parseInt(idSessao)).getListaDeItens();
		if(listaDeItens.isEmpty()){
			return "O usuário não possui itens cadastrados";
		}else{
			for (Item item : listaDeItens) {
				itens += item.getNome() + "; ";
			}

		}
		itens = itens.substring(0, itens.length() - 2);
		return itens;

	}

	public String getItens(String idSessao, String login) throws Exception{
		if(verificaAmizade(idSessao, login).equals("false")){
			throw new Exception("O usuário não tem permissão para visualizar estes itens");
		}
		if(!(Validador.validarString(login)))
			throw new Exception("Login inválido");
		verificarSessao(idSessao);
		String itens = "";
		Usuario alvo = null;
		try{
			alvo = retornaUsuarioPeloLogin(login);
		}catch(Exception e){
			throw new Exception("Login inexistente");
		}
		List<Item> listaDeItens = alvo.getListaDeItens();
		if(listaDeItens.isEmpty()){
			return "O usuário não possui itens cadastrados";
		}else{
			for (Item item : listaDeItens) {
				itens += item.getNome() + "; ";
			}

		}
		itens = itens.substring(0, itens.length() - 2);
		return itens;


	}

	public Integer requisitarEmprestimo(String idSessao, String idItem, int duracaoDoEmprestimo) throws Exception{
		verificarSessao(idSessao);
		verificarIdItem(idItem);
		Usuario beneficiado = listaDeSessoesAbertas.get(Integer.parseInt(idSessao));
		Item itemAemprestar = retornaItemPeloId(Integer.parseInt(idItem));
		Usuario emprestador = itemAemprestar.getDono();
		if(!beneficiado.ehAmigo(emprestador)){
			throw new Exception("O usuário não tem permissão para requisitar o empréstimo deste item");
		}
		int idRequisicaoEmprestimo = gerarId();
		List<IGenericObject> emprestimos = gerenciadorEmprestimos.pesquisar("idRequisicaoEmprestimo", idRequisicaoEmprestimo + "");
		while(emprestimos.size() > 0){
			idRequisicaoEmprestimo = gerarId();
			emprestimos = gerenciadorEmprestimos.pesquisar("idRequisicaoEmprestimo" ,idRequisicaoEmprestimo + "");
		}
		Emprestimo emprestimo = EmprestimoFactory.createEmprestimo(beneficiado, emprestador, itemAemprestar ,(duracaoDoEmprestimo), idRequisicaoEmprestimo);
		emprestador.adicionarEmprestimoPendente(emprestimo);
		/*envia uma mensagem*/
		Mensagem m = new Mensagem(emprestador, beneficiado, itemAemprestar);
		enviarMensagem(m);
		gerenciadorEmprestimos.cadastrar(emprestimo);
		return idRequisicaoEmprestimo;

	}

	public Integer aprovarEmprestimo(String idSessao, String idRequisicaoEmprestimo) throws Exception{
		verificarSessao(idSessao);
		verificarIdRequisicaoEmprestimo(idRequisicaoEmprestimo);
		Emprestimo emprestimoParaAceitar = null;
		Usuario emprestador = retornaUsuarioPeloIdSessao(idSessao);
		List<IGenericObject> emprestimos = gerenciadorEmprestimos.pesquisar("idRequisicaoEmprestimo", idRequisicaoEmprestimo + "");
		Usuario beneficiado = ((Emprestimo)emprestimos.get(0)).getBeneficiado();
		emprestimoParaAceitar = (Emprestimo) emprestimos.get(0);
		Item itemAEmprestar = emprestimoParaAceitar.getItemAEmprestar();
		if(!listaDeSessoesAbertas.get(Integer.parseInt(idSessao)).equals(emprestimoParaAceitar.getEmprestador())){
			throw new Exception("O empréstimo só pode ser aprovado pelo dono do item");
		}
		int idEmprestimo = gerarId();
		List<IGenericObject> emprestimosCadastrados = gerenciadorEmprestimos.pesquisar("idEmprestimo", idEmprestimo + "");
		while(emprestimosCadastrados.size() > 0){
			idEmprestimo = gerarId();
			emprestimosCadastrados = gerenciadorEmprestimos.pesquisar("idEmprestimo" ,idEmprestimo + "");
		}
		emprestador.aprovarEmprestimo(emprestimoParaAceitar);
	//TODO	emprestimoParaAceitar.setDataDeAprovacao(new Data(dataSistema.get(GregorianCalendar.DAY_OF_MONTH)+"/"+dataSistema.get(GregorianCalendar.MONTH)+"/"+dataSistema.get(GregorianCalendar.YEAR)));
		emprestimoParaAceitar.setDataDeAprovacao(new Data(dataSistema.dataFormatada()));
		emprestimoParaAceitar.setDataDeDevolucao(emprestimoParaAceitar.calculaDataDevolucao());
		beneficiado.addEmprestimoDoBeneficiado(emprestimoParaAceitar);
		itemAEmprestar.setStatusItem(StatusItem.EMPRESTADO);
		emprestimoParaAceitar.setidEmprestimo(idEmprestimo);
		gerenciadorEmprestimos.alterar(emprestimoParaAceitar); // atualiza o emprestimo na lista de emprestimos.
		gerenciadorItems.alterar(emprestimoParaAceitar.getItemAEmprestar());
		if (emprestimoParaAceitar.getItemAEmprestar().getListaDeUsuariosInteressados().contains(beneficiado)){
			emprestimoParaAceitar.getItemAEmprestar().getListaDeUsuariosInteressados().remove(beneficiado);
		}
		Atividade atividade = new AtividadeEmprestimoEmAndamento(emprestador.getNome(), itemAEmprestar.getNome(), beneficiado.getNome());
		_historico.addAtividades(emprestador, atividade);
		return idEmprestimo;
	}

	public void adicionarDias(int dias){
		//TODO this.dataSistema.add(GregorianCalendar.DAY_OF_MONTH, dias);
		dataSistema.addDias(dias);
	}

	public String getEmprestimos(String idSessao, String tipo) throws Exception{
		verificarSessao(idSessao);
		if(!(Validador.validarString(tipo))){
			throw new Exception("Tipo inválido");
		}
		if((!tipo.equals("emprestador") && (!tipo.equals("beneficiado") && (!tipo.equals("todos"))))){
			throw new Exception("Tipo inexistente");
		}
		Usuario usuario = listaDeSessoesAbertas.get(Integer.parseInt(idSessao));
		String resultado = "";
		List<Emprestimo> emprestimos = usuario.getEmprestimos(tipo);
		List<Emprestimo> emprestimosEmprestador = new ArrayList<Emprestimo>();
		List<Emprestimo> emprestimosBeneficiado = new ArrayList<Emprestimo>();
		if(emprestimos.isEmpty()){
			return "Não há empréstimos deste tipo" ;
		}else{
			for (Emprestimo emprestimo : emprestimos) {
				if(usuario.getListaEmprestimosDoEmprestador().contains(emprestimo)){
					emprestimosEmprestador.add(emprestimo);
				}else if(usuario.getListaEmprestimosDoBeneficiado().contains(emprestimo)){
					emprestimosBeneficiado.add(emprestimo);
				}
			}
			for(Emprestimo emprestimo: emprestimosEmprestador){
				resultado += emprestimo.getEmprestador().getLogin() + "-" + emprestimo.getBeneficiado().getLogin() + 
						":" + emprestimo.getItemAEmprestar().getNome() + ":" + emprestimo.getStatus().getNomeStatus() + "; ";
			}
			for(Emprestimo emprestimo: emprestimosBeneficiado){
				resultado += emprestimo.getEmprestador().getLogin() + "-" + emprestimo.getBeneficiado().getLogin() + 
						":" + emprestimo.getItemAEmprestar().getNome() + ":" + emprestimo.getStatus().getNomeStatus() + "; ";
			}
		}
		resultado = resultado.substring(0, resultado.length() - 2);
		return resultado;

	}


	public void devolverItem(String idSessao, String idEmprestimo) throws Exception{
		verificarSessao(idSessao);
		verificarIdEmprestimo(idEmprestimo);
		Emprestimo emprestimo = (Emprestimo)(gerenciadorEmprestimos.pesquisar("idEmprestimo", idEmprestimo)).get(0);
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		if(!(usuario.equals(emprestimo.getBeneficiado())))
			throw new Exception("O item só pode ser devolvido pelo usuário beneficiado");
		if(emprestimo.getStatus().equals(StatusEmprestimo.COMPLETADO_SC) || 
				emprestimo.getStatus().equals(StatusEmprestimo.COMPLETADO))
			throw new Exception("Item já devolvido");
		if(!emprestimo.getStatus().equals(Emprestimo.StatusEmprestimo.CANCELADO))
			emprestimo.setStatus(StatusEmprestimo.COMPLETADO_SC);
		emprestimo.getItemAEmprestar().setStatusItem(Item.StatusItem.DISPONIVEL);
		gerenciadorItems.alterar(emprestimo.getItemAEmprestar());
		gerenciadorEmprestimos.alterar(emprestimo); // atualiza o emprestimo na lista de emprestimos cadastrados no sistema.

	}

	public void confirmarTerminoEmprestimo(String idSessao, String idEmprestimo) throws Exception{
		verificarSessao(idSessao);
		verificarIdEmprestimo(idEmprestimo);
		Emprestimo emprestimo = (Emprestimo)(gerenciadorEmprestimos.pesquisar("idEmprestimo", idEmprestimo)).get(0);
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		if(!(usuario.equals(emprestimo.getItemAEmprestar().getDono())))
			throw new Exception("O término do empréstimo só pode ser confirmado pelo dono do item");
		if(emprestimo.getStatus().equals(StatusEmprestimo.COMPLETADO))
			throw new Exception("Término do empréstimo já confirmado");
		List<Usuario> listaUser = emprestimo.getItemAEmprestar().getListaDeUsuariosInteressados();
		//		A mensagem privada (offtopic) enviada tem o seguinte formato:
		//			Assunto: O item <nome_do_item>  do usuário <usuario_dono_do_item>
		//			está disponível;
		//		Mensagem: Agora você pode requisitar o empréstimo do <nome_do_item>.
		String assunto = "O item "+emprestimo.getItemAEmprestar().getNome()+" do usuário "+usuario.getNome()+" está disponível";
		String corpoMensagem = "Agora você pode requisitar o empréstimo do "+emprestimo.getItemAEmprestar().getNome();
		for(Usuario user : listaUser){	
			Mensagem msg = new Mensagem(assunto, corpoMensagem, user, usuario);
			enviarMensagem(msg);
		}
		if(!(emprestimo.getStatus().equals(StatusEmprestimo.CANCELADO))){
			emprestimo.setStatus(StatusEmprestimo.COMPLETADO);
		}
		emprestimo.getItemAEmprestar().setStatusItem(StatusItem.DISPONIVEL);
		usuario.adicionaEmprestimoRealizado();
		gerenciadorUsuarios.alterar(usuario);
		gerenciadorEmprestimos.alterar(emprestimo); // atualiza o emprestimo na lista de emprestimos cadastrados no sistema.
		Atividade atividade = new AtividadeTerminodeEmprestimo(usuario.getNome(), emprestimo.getItemAEmprestar().getNome());
		_historico.addAtividades(usuario, atividade);
	}


	public void desfazerAmizade(String idSessao, String loginDoAmigo) throws Exception{
		if(!Validador.validarString(loginDoAmigo)){
			throw new Exception("Login inválido");
		}
		try{
			retornaUsuarioPeloLogin(loginDoAmigo);
		}catch(Exception e){
			throw new Exception("Usuário inexistente");
		}
		verificarSessao(idSessao);
		Usuario usuario = listaDeSessoesAbertas.get(Integer.parseInt(idSessao));
		Usuario amigoDoUsuario = (Usuario) (gerenciadorUsuarios.pesquisar("login", loginDoAmigo)).get(0);
		if(!usuario.ehAmigo(amigoDoUsuario)){
			throw new Exception("Amizade inexistente");
		}
		usuario.getListaDeAmigosAdicionados().remove(amigoDoUsuario);
		amigoDoUsuario.getListaDeAmigosAdicionados().remove(usuario);
		List<Emprestimo> emprestimosPendentesDoUser = usuario.getListaEmprestimosPendentes();	
		List<Emprestimo> emprestimosPendentesDoAmigo = amigoDoUsuario.getListaEmprestimosPendentes();		
		for(Iterator<Emprestimo> it = emprestimosPendentesDoUser.iterator(); it.hasNext();){ // As requisições de empréstimo do amigo para o usuario sao removidas,caso haja alguma.
			Emprestimo emprestimo = it.next();
			if(emprestimo.getBeneficiado().equals(amigoDoUsuario)){
				it.remove();
				gerenciadorEmprestimos.deletar(emprestimo);
				break;
			}
		}
		for(Iterator<Emprestimo> it = emprestimosPendentesDoAmigo.iterator(); it.hasNext();){ // As requisições de empréstimo do usuario para o amigo sao removidas, caso haja alguma.
			Emprestimo emprestimo = it.next();
			if(emprestimo.getBeneficiado().equals(usuario)){
				it.remove();
				gerenciadorEmprestimos.deletar(emprestimo);
				break;
			}
		}
		gerenciadorUsuarios.alterar(usuario); // atualiza o usuario na lista de usuarios cadastrados no sistema.
		gerenciadorUsuarios.alterar(amigoDoUsuario);// atualiza o usuario na lista de usuarios cadastrados no sistema.

	}

	public void apagarItem(String idSessao, String idItem) throws Exception{ //US13
		verificarSessao(idSessao);
		verificarIdItem(idItem);
		Item itemApagado = retornaItemPeloId(Integer.parseInt(idItem));
		Usuario usuario = listaDeSessoesAbertas.get(Integer.parseInt(idSessao));
		if(!(usuario.getListaDeItens().contains(itemApagado))){
			throw new Exception("O usuário não tem permissão para apagar este item");
		}
		if(itemApagado.getStatusDeItem().equals(StatusItem.EMPRESTADO)){
			throw new Exception("O usuário não pode apagar este item enquanto estiver emprestado");
		}
		usuario.getListaDeItens().remove(itemApagado);
		gerenciadorItems.deletar(itemApagado);
		List<Emprestimo> emprestimosPendentesDoUser = usuario.getListaEmprestimosPendentes();		 
		for(Iterator<Emprestimo> it = emprestimosPendentesDoUser.iterator(); it.hasNext();){
			Emprestimo emprestimo = it.next();
			if(emprestimo.getItemAEmprestar().equals(itemApagado)){
				it.remove();
				gerenciadorEmprestimos.deletar(emprestimo);
				break;
			}

		}

	}
	public String getRanking(String idSessao,String categoria) throws Exception{
		verificarSessao(idSessao);
		if(!(Validador.validarString(categoria))){
			throw new Exception("Categoria inválida");
		}

		if(!categoria.equals("amigos") && !categoria.equals("global")){
			throw new Exception("Categoria inexistente");
		}

		Usuario user = getListaDeSessoesAbertas().get(Integer.parseInt(idSessao));
		Ranking rank = new Ranking();
		return rank.getRanking(user, categoria);
	}

	public void encerrarSistema(){

	}

	public void registrarInteresse(String idSessao, String idItem) throws Exception {
		verificarSessao(idSessao);
		verificarIdItem(idItem);
		Usuario interessado = retornaUsuarioPeloIdSessao(idSessao);
		Item it = retornaItemPeloId(Integer.parseInt(idItem));
		for(Usuario user1 : it.getListaDeUsuariosInteressados()){
			if(user1.equals(interessado)){
				throw new Exception("O usuário já registrou interesse neste item");
			}
		}
		if(it.getStatusDeItem().equals(Item.StatusItem.DISPONIVEL)){
			throw new Exception("O usuário não tem permissão para registrar interesse neste item");
		}
		if (it.getDono().equals(interessado)){
			throw new Exception("O usuário não pode registrar interesse no próprio item");
		}
		it.setAdicionarUsuarioInteressado(interessado);
		Atividade atividade = new AtividadeRegistroDeInteresseEmItem(interessado.getNome(), it.getNome(), it.getDono().getNome());
		_historico.addAtividades(interessado, atividade);
		_historico.addAtividades(it.getDono(), atividade);

	}

	public String enviarMensagem(String idSessao, String destinatario, String assunto, String mensagem) throws Exception{
		verificarSessao(idSessao);
		Usuario remetente = listaDeSessoesAbertas.get(Integer.parseInt(idSessao));
		Usuario dest = null;
		if(!(Validador.validarString(destinatario)))
			throw new Exception("Destinatário inválido");
		try{
			dest = retornaUsuarioPeloLogin(destinatario);	
		}catch(Exception e){
			throw new Exception("Destinatário inexistente");
		}

		Validador.validarString(assunto);
		Validador.validarString(mensagem);
		Mensagem msg = new Mensagem(assunto, mensagem, dest, remetente);
		return GerenciadorDeMensagens.getInstance().enviarMensagem(msg);
	}

	public String enviarMensagem(String idSessao, String destinatario, String assunto, String mensagem, String idRequisicaoEmprestimo) throws Exception{
		if(!(Validador.validarString(idRequisicaoEmprestimo))){
			throw new Exception("Identificador da requisição de empréstimo é inválido"); //Aqui
		}
		if(!(Validador.validarString(assunto)))
			throw new Exception("Assunto inválido");
		if(!(Validador.validarString(mensagem)))
			throw new Exception("Mensagem inválida");
		verificarSessao(idSessao);
		Usuario remetente = listaDeSessoesAbertas.get(Integer.parseInt(idSessao));
		Usuario dest = null;
		if(!(Validador.validarString(destinatario)))
			throw new Exception("Destinatário inválido");
		try{
			dest = retornaUsuarioPeloLogin(destinatario);	
		}catch(Exception e){
			throw new Exception("Destinatário inexistente");
		}
		verificarIdRequisicaoEmprestimo(idRequisicaoEmprestimo);
		List<IGenericObject> emprestimos = gerenciadorEmprestimos.pesquisar("idRequisicaoEmprestimo", idRequisicaoEmprestimo + "");
		if(!remetente.equals(((Emprestimo)emprestimos.get(0)).getBeneficiado()) &&
				!remetente.equals(((Emprestimo)emprestimos.get(0)).getEmprestador()))
			throw new Exception("O usuário não participa deste empréstimo");
		Validador.validarString(assunto);
		Validador.validarString(mensagem);
		Mensagem msg = new Mensagem(assunto, mensagem, dest, remetente);
		msg.setEhPadrao(true);
		return GerenciadorDeMensagens.getInstance().enviarMensagem(msg);
	}

	public void enviarMensagem(Mensagem m){
		GerenciadorDeMensagens.getInstance().enviarMensagem(m);
	}

	public String lerMensagens(String idSessao, String idTopico) throws Exception{
		verificarSessao(idSessao);
		if(!(Validador.validarString(idTopico)))
			throw new Exception("Identificador do tópico é inválido");
		if(GerenciadorDeMensagens.getInstance().getTopicos().containsKey(idTopico)){
			HashMap<String, List<Mensagem>> mapa = (HashMap<String, List<Mensagem>>)GerenciadorDeMensagens.getInstance().organizarTopicos(GerenciadorDeMensagens.getInstance().getTodasAsMensagens());
			if(!(mapa.get(GerenciadorDeMensagens.getInstance().getTopicos().get(idTopico)).get(0).getDestinatario().equals(getListaDeSessoesAbertas().get(Integer.parseInt(idSessao)))) &&
					!(mapa.get(GerenciadorDeMensagens.getInstance().getTopicos().get(idTopico)).get(0).getRemetente().equals(getListaDeSessoesAbertas().get(Integer.parseInt(idSessao)))))
				throw new Exception("O usuário não tem permissão para ler as mensagens deste tópico");
		}
		return GerenciadorDeMensagens.getInstance().lerMensagensDeUmTopico(idTopico);
	}


	public String lerTopicos(String idSessao, String tipo) throws Exception{
		verificarSessao(idSessao);
		Usuario user = listaDeSessoesAbertas.get(Integer.parseInt(idSessao));

		if(!Validador.validarString(tipo)){
			throw new Exception("Tipo inválido");
		}

		if(!tipo.equalsIgnoreCase("negociacao") && !tipo.equalsIgnoreCase("offtopic") && !tipo.equalsIgnoreCase("todos")){
			throw new Exception("Tipo inexistente");
		}

		Map<String, List<Mensagem>> topicos = null;

		if(tipo.equalsIgnoreCase("negociacao")){
			topicos = GerenciadorDeMensagens.getInstance().organizarTopicos(GerenciadorDeMensagens.getInstance().getMensagensPadrao(true, GerenciadorDeMensagens.getInstance().getTodasAsMensagens(user)));
		}

		if(tipo.equalsIgnoreCase("offtopic")){
			topicos = GerenciadorDeMensagens.getInstance().organizarTopicos(GerenciadorDeMensagens.getInstance().getMensagensPadrao(false, GerenciadorDeMensagens.getInstance().getTodasAsMensagens(user)));
		}

		if(tipo.equalsIgnoreCase("todos")){
			topicos = GerenciadorDeMensagens.getInstance().organizarTopicos(GerenciadorDeMensagens.getInstance().getTodasAsMensagens(user));
		}

		if(topicos.isEmpty()){
			return "Não há tópicos criados"; //remover
		}

		return GerenciadorDeMensagens.getInstance().lerTopicos(topicos, user);

	}

	public String pesquisarItem(String idSessao, String chave, String atributo,
			String tipoOrdenacao, String criterioOrdenacao) throws Exception {
		verificarSessao(idSessao);
		Validador.validarPesquisarItem(chave, atributo, tipoOrdenacao, criterioOrdenacao);
		List<IGenericObject> lista =  gerenciadorItems.pesquisar(atributo, chave);
		Usuario pesquisador = getListaDeSessoesAbertas().get(Integer.parseInt(idSessao));
		for (int i = 0 ; i < lista.size() ; i++){
			Usuario donoDoitem = ((Item)lista.get(i)).getDono();
			if(!(donoDoitem.ehAmigo(pesquisador)))
				lista.remove(i);
		}
		if (lista.size()==0){
			return "Nenhum item encontrado";
		}
		return OrdenaPesquisa.ordena(tipoOrdenacao, criterioOrdenacao, lista);
	}

	public void requisitarDevolucao(String idSessao, String idEmprestimo) throws Exception{
		verificarSessao(idSessao);
		if(!(Validador.validarString(idEmprestimo)))
			throw new Exception("Identificador do empréstimo é inválido");
		try{
			Integer.parseInt(idEmprestimo);
		}catch(Exception e){
			throw new Exception("Empréstimo inexistente");
		}
		Usuario usuario = getListaDeSessoesAbertas().get(Integer.parseInt(idSessao));
		int contem = 0;
		for(Emprestimo e : usuario.getEmprestimos("todos"))
			if(e.getIdEmprestimo() == Integer.parseInt(idEmprestimo)){
				if(e.getItemAEmprestar().getStatusDeItem().equals(Item.StatusItem.DISPONIVEL))
					throw new Exception("Item já devolvido");
				if(e.getStatus().equals(Emprestimo.StatusEmprestimo.CANCELADO))
					throw new Exception("Devolução já requisitada");
			//TODO	if(e.getDataDeDevolucao().compararData(new Data(dataSistema.get(GregorianCalendar.DAY_OF_MONTH)+"/"+dataSistema.get(GregorianCalendar.MONTH)+"/"+dataSistema.get(GregorianCalendar.YEAR)))<0){
			//		e.setStatus(StatusEmprestimo.CANCELADO);
			//		Mensagem m = new Mensagem("Empréstimo do item "+e.getItemAEmprestar().getNome()+ " a "+e.getBeneficiado().getNome(), e.getEmprestador().getNome()+" solicitou a devolução do item "+e.getItemAEmprestar().getNome(), e.getBeneficiado(), e.getEmprestador());
			//		enviarMensagem(m);
			//	}
				if(dataSistema.getDate().after(e.getDataDeDevolucao().getDate())){
					e.setStatus(StatusEmprestimo.CANCELADO);
					Mensagem m = new Mensagem("Empréstimo do item "+e.getItemAEmprestar().getNome()+ " a "+e.getBeneficiado().getNome(), e.getEmprestador().getNome()+" solicitou a devolução do item "+e.getItemAEmprestar().getNome(), e.getBeneficiado(), e.getEmprestador());
					enviarMensagem(m);
				}
				contem =1;
			}
		if (contem == 0)
			throw new Exception("O usuário não tem permissão para requisitar a devolução deste item");
	}

	public String exbirHistoricoAtividadesDoUsuario(String idSessao) throws Exception{
		verificarSessao(idSessao);
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		String atividades = _historico.imprimeAtividadesDoUsuario(usuario);
		return atividades;
	}

	public String exibirHistoricoDeAtividadesConjunto(String idSessao) throws Exception{
		verificarSessao(idSessao);
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		String atividadesConjuntas = "";
		atividadesConjuntas += _historico.imprimeAtividadesConjuntas(usuario);
		return atividadesConjuntas;

	}

	public void zerarSistema(){
		_repositorio.getUsuarios().clear();
		_repositorio.getItens().clear();
		this.listaDeSessoesAbertas.clear();
		HistoricoDeAtividades.getTodasAtividades().clear();
		GerenciadorDeMensagens.zerarGerenciador();
	}
	public String publicarPedido(String idSessao, String nomeItem,
			String descricaoItem) throws Exception {
		verificarSessao(idSessao);
		if(!Validador.validarString(nomeItem)){
			throw new Exception("Nome inválido");
		}
		if(!Validador.validarString(descricaoItem)){
			throw new Exception("Descrição inválida");
		}
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		GerenciadorPedidos gerenciador = new GerenciadorPedidos();
		return gerenciador.publicarPedido(usuario, nomeItem, descricaoItem);
		
	}
	public void rePublicarPedido(String idSessao, String idPublicacaoPedido) throws Exception {
		verificarSessao(idSessao);
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		GerenciadorPedidos gerenciador = new GerenciadorPedidos();	
		gerenciador.republicarPedido(usuario, idPublicacaoPedido);
	}
	
	public void oferecerItem(String idSessao, String idPublicacaoPedido, String idItem) throws Exception {
		verificarSessao(idSessao);
		verificarPublicacaoPedido(idPublicacaoPedido);
		verificarIdItem(idItem);

		Item item = retornaItemPeloId(Integer.parseInt(idItem));

		AtividadeDePedirItem atividade = null;
		HistoricoDeAtividades.getInstance();
		Collection<Atividade> todasAtividades = HistoricoDeAtividades.getTodasAtividades().values();
		for(Atividade ativ : todasAtividades){
			if (ativ instanceof AtividadeDePedirItem){
				if(ativ.getIdAtividade().equals(idPublicacaoPedido)){
					atividade = (AtividadeDePedirItem) ativ;
				}
			}
		}
		if(atividade!= null){
			Usuario usuario = getListaDeSessoesAbertas().get(Integer.parseInt(idSessao));
			Usuario publicador = retornaUsuarioPeloNome(atividade.getNomeDoUsuario());
		
			GerenciadorPedidos gerenciador = new GerenciadorPedidos();
			gerenciador.oferecerPedido(usuario, publicador, item);
		}
		
	}
	
	public String localizarUsuario(String idSessao) throws Exception{
		verificarSessao(idSessao);
		Usuario usuario = retornaUsuarioPeloIdSessao(idSessao);
		LocalizadorDeUsuario localizador = new LocalizadorDeUsuario();
		return localizador.localizarUsuario(usuario);
		
	}
}
